package com.seer.auto.utils;

import com.seer.auto.dao.AbstractDao;
import com.seer.auto.entity.Column;
import com.seer.auto.entity.Entity;
import com.seer.auto.entity.MetaEntity;
import com.seer.auto.entity.Table;
import com.seer.auto.entity.factory.MapEntityFactory;
import java.io.IOException;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.NotFoundException;

public class ClassUtils {

    public static Class<?> makeFactory(String allName, MetaEntity<?> meta)
        throws NotFoundException, CannotCompileException {
        CtClass cc = POOL.makeClass(allName);
        CtClass spClass = POOL.get(MapEntityFactory.class.getName());
        cc.setInterfaces(new CtClass[]{spClass});
        CtMethod cm = CtNewMethod.copy(spClass.getDeclaredMethods()[0], cc, null);
        StringBuilder code = new StringBuilder();
        code.append("{");
        code.append(meta.getClazz().getName()).append(" obj = new ")
            .append(meta.getClazz().getName()).append("();\n");
        for (Column c : meta.getTable().getColumns()) {
            code.append("obj.")
                .append(c.setMethod()).append("((")
                .append(c.getJavaType()).append(")")
                .append("$1.get(\"")
                .append(c.getName()).append("\"));\n");
        }
        code.append("return obj;\n}");
        cm.setBody(code.toString());
        cc.addMethod(cm);
        try {
            cc.writeFile("C:\\Users\\wangxianzhi\\Desktop\\auto\\src\\main\\java");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return cc.toClass();
    }

    private final static ClassPool POOL = ClassPool.getDefault();

    public static String scoreName(String name) {
        StringBuilder sb = new StringBuilder();
        final byte[] bytes = name.getBytes();
        if (bytes[0] >= 'A' && bytes[0] <= 'Z') {
            sb.append((char) (bytes[0] + 32));
        }
        for (int i = 1; i < bytes.length; i++) {
            if (bytes[i] >= 'A' && bytes[i] <= 'Z') {
                sb.append("_");
                sb.append((char) (bytes[i] + 32));
            } else {
                sb.append((char) bytes[i]);
            }
        }

        return sb.toString();
    }

    public static String className(String name) {
        final byte[] bytes = name.getBytes();
        bytes[0] -= 32;
        int i, j;
        for (i = 1, j = 1; i < bytes.length; ) {
            if ('_' == bytes[i]) {
                bytes[++i] -= 32;
            }
            bytes[j++] = bytes[i++];
        }
        return new String(bytes, 0, j);
    }

    public static String lowerFirstCase(String name) {
        final byte[] bytes = name.getBytes();
        if (bytes[0] >= 'A' && bytes[0] <= 'Z') {
            bytes[0] += 32;
        }
        return new String(bytes);
    }

    public static CtField getField(Column c, CtClass cc)
        throws NotFoundException, CannotCompileException {
        CtField field = new CtField(POOL.get(SqlUtils.getJavaType(c.getDataType())),
            lowerFirstCase(className(c.getName())), cc);
        cc.addField(field);
        return field;
    }

    /**
     * create an entity by EntityName and Table Struct
     *
     * @param allName the all name of the entity
     * @param t       the table of the entity
     * @return
     * @throws NotFoundException
     * @throws CannotCompileException
     */
    public static Class<?> newEntity(String allName, Table t)
        throws NotFoundException, CannotCompileException {
        CtClass cc = POOL.makeClass(allName);
        CtClass spClass = POOL.get(Entity.class.getName());
        cc.setSuperclass(spClass);
        for (Column c : t.getColumns()) {
            CtField param = getField(c, cc);
            cc.addMethod(CtNewMethod.setter(c.setMethod(), param));
            cc.addMethod(CtNewMethod.getter(c.getMethod(), param));
        }
        CtConstructor cons = new CtConstructor(new CtClass[]{}, cc);
        StringBuilder code = new StringBuilder();
        code.append("{\nsuper(\"").append(t.getName()).append("\", ").append(t.getColumns().size())
            .append(");\n}\n");
        cons.setBody(code.toString());
        cc.addConstructor(cons);
        try {
            cc.writeFile("C:\\Users\\wangxianzhi\\Desktop\\auto\\src\\main\\java");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return cc.toClass();
    }


    public static Class<?> newSQLTemplate(String allName, MetaEntity<?> meta)
        throws NotFoundException, CannotCompileException {
        CtClass cc = POOL.makeClass(allName);
        CtClass spClass = POOL.get(AbstractDao.class.getName());
        cc.setSuperclass(spClass);
        cc.setGenericSignature(meta.getClazz().getName());
        CtMethod[] ms = spClass.getDeclaredMethods();
        for (CtMethod m : ms) {
            CtMethod cm = CtNewMethod.copy(m, cc, null);
            switch (m.getName()) {
                case "list":
                    newList(cm, meta);
                    break;
                case "get":
                    newGet(cm, meta);
                    break;
                case "update":
                    newUpdate(cm, meta);
                    break;
                case "create":
                    newInsert(cm, meta);
                    break;
                case "delete":
                    newDelete(cm, meta);
                    break;
                case "count":
                    newCount(cm, meta);
                    break;
                default:
                    continue;
            }
            cc.addMethod(cm);
        }
        CtConstructor con = new CtConstructor(new CtClass[]{}, cc);
        con.setBody("{super();\n}");
        cc.addConstructor(con);
        try {
            cc.writeFile("C:\\Users\\wangxianzhi\\Desktop\\auto\\src\\main\\java");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return cc.toClass();
    }

    private static void newCount(CtMethod cm, MetaEntity<?> meta) throws CannotCompileException {
        cm.setBody(SqlUtils.countMethod(meta));
    }

    private static void newList(CtMethod cm, MetaEntity<?> meta) throws CannotCompileException {
        cm.setBody(SqlUtils.listMethod(meta));
    }

    private static void newGet(CtMethod cm, MetaEntity<?> meta) throws CannotCompileException {
        cm.setBody(SqlUtils.getMethod(meta));
    }

    private static void newUpdate(CtMethod cm, MetaEntity<?> meta) throws CannotCompileException {
        cm.setBody(SqlUtils.updateMethod(meta));
    }

    private static void newInsert(CtMethod cm, MetaEntity<?> meta) throws CannotCompileException {
        cm.setBody(SqlUtils.insertMethod(meta));
    }

    private static void newDelete(CtMethod cm, MetaEntity<?> meta) throws CannotCompileException {
        cm.setBody(SqlUtils.deleteMethod(meta));
    }
}


